#include "tts_client.h"

#include "WLcJSON.h"
#include "wl_http_client.h"
#include "protocol_client.h"
#include "repo.h"
#include "weilapb.h"
#include "weilapb_defines.h"
#include "wl_ext_fs.h"
#include "wlsdk_define.h"

#ifdef __cplusplus
extern "C" {
#endif

#define TTS_CLIENT_TIMER_INTERVAL 1  // 1秒
#define TTS_CLIENT_TIMEOUT 30        // 超时时间
#define TTS_CLIENT_MAX_REQUEST 10

typedef enum {
    TTS_CLIENT_REQUEST_INIT = 0,   //初始化
    TTS_CLIENT_REQUEST_SYNTHESIS,  //合成
    TTS_CLIENT_REQUEST_DOWNLOAD    //下载
} tts_client_request_state;

typedef struct __tts_client_request {
    wl_uint16           seq;
    wl_uint16           state;
    wl_int32            timeout;  //请求超时时间 0 不限
    wl_int32            lifeleft;
    tts_client_callback callback;
    void*               context;
    wl_int32            type;
    wl_char             url[MAX_URL_LEN];
    wl_uint16           text_len;
    wl_char             text[MAX_TTS_TEXT_LEN];
    wl_uint16           content_len;
    wl_char             content[ZERO_CONTENT_SIZE];
} tts_client_request;

typedef struct __protocol_client_global {
    wl_bool   initialized;
    wl_list   request_list;
    WL_TIMER  timer;
    wl_uint16 request_seq;
} tts_client_global;

static tts_client_global s_tts_client_global;

static wl_int32 execute_request(void);
static void     request_error_callback(tts_client_request* request, wl_int32 result_code);
static void     tts_http_client_callback(wl_uint32 result,
                                         wl_char*  content,
                                         wl_uint32 content_len,
                                         wl_uint32 content_type,
                                         void*     context);

static wl_int32 process_tts_synthesis(tts_client_request* request, wl_char* content, wl_uint32 content_len) {
    wl_int32 resultcode = WLSDK_RESULT_LOCAL_HTTP_ERROR;
    //合成状态,下载合成文件
    WLcJSON* store = WLcJSON_Parse(content);
    if (store) {
        WLcJSON* errcode_obj = WLcJSON_GetObjectItem(store, "errcode");
        if (errcode_obj) {
            resultcode = WLcJSON_GetNumberValue(errcode_obj);
            if (WLSDK_RESULT_SUCCESS == resultcode) {
                WLcJSON* data_obj = WLcJSON_GetObjectItem(store, "data");
                if (data_obj) {
                    WLcJSON* ptturl_obj = WLcJSON_GetObjectItem(data_obj, "ptt_url");
                    WLcJSON* url_obj    = WLcJSON_GetObjectItem(data_obj, "url");
                    wl_char* url        = wl_null;
                    if (ptturl_obj && WLcJSON_IsString(ptturl_obj)) {
                        url = WLcJSON_GetStringValue(ptturl_obj);
                    } else if (url_obj && WLcJSON_IsString(url_obj)) {
                        url = WLcJSON_GetStringValue(url_obj);
                    }
                    if (url) {
                        if (0
                            != wl_http_client_get(url,
                                               HTTP_CLIENT_REQUEST_TIMEOUT_30,
                                               tts_http_client_callback,
                                               (void*)((int)request->seq))) {
                            resultcode = WLSDK_RESULT_LOCAL_HTTP_ERROR;
                        } else {
                            request->state = TTS_CLIENT_REQUEST_DOWNLOAD;
                        }
                    } else {
                        resultcode = WLSDK_RESULT_LOCAL_DATA_INVALID;
                    }
                }
            }
        }
        WLcJSON_Delete(store);
    }

    return resultcode;
}

static wl_char* tts_read_file_data(wl_char* file_path, wl_int32* content_len) {
    //读取文件
    wl_int32   buffer_size = 0;
    wl_char*   buffer      = wl_null;
    wl_size    readlen     = 0;
    wl_ext_fs_handle fhandle     = WL_EXT_INVALID_FS;
    wl_ssize   ret         = 0;
    wl_uint32  pos         = 0;

    if (wl_null == file_path || wl_null == content_len) {
        WLLOGW("file path is null");
        return wl_null;
    }
    fhandle = wl_ext_fs_open(file_path, WL_EXT_RDONLY);
    //文件不存在
    if (WL_EXT_INVALID_FS == fhandle) {
        WLLOGW("open file %s fail", file_path);
        wl_ext_fs_close(fhandle);
        return wl_null;
    }
    buffer_size = wl_ext_fs_size_by_handle(fhandle);
    if (0 >= buffer_size) {
        WLLOGW("file %s empty", file_path);
        wl_ext_fs_close(fhandle);
        return wl_null;
    }
    *content_len = buffer_size;
    buffer       = (wl_char*)wl_malloc(buffer_size);
    if (wl_null == buffer) {
        WLLOGW("malloc buffer_size %d fail", buffer_size);
        wl_ext_fs_close(fhandle);
        return wl_null;
    }
    readlen = buffer_size > 4096 ? 4096 : buffer_size;
    while (0 < (ret = wl_ext_fs_read(fhandle, buffer + pos, readlen))) {
        pos += ret;
    }
    if (0 > ret) {
        WLLOGW("store data len %d invalid", ret);
        wl_ext_fs_close(fhandle);
        wl_free(buffer);
        return wl_null;
    }
    wl_ext_fs_close(fhandle);
    return buffer;
}

static void tts_http_client_callback(wl_uint32 result,
                                     wl_char*  content,
                                     wl_uint32 content_len,
                                     wl_uint32 content_type,
                                     void*     context) {
    wl_list_iterator    iterator;
    tts_client_request* request = (tts_client_request*)wl_list_get_first(&s_tts_client_global.request_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        if ((int)context == request->seq) {
            if (WLSDK_RESULT_SUCCESS == result) {
                wl_int32 resultcode = WLSDK_RESULT_LOCAL_HTTP_ERROR;
                //合成状态,下载合成文件
                if (TTS_CLIENT_REQUEST_SYNTHESIS == request->state) {
                    resultcode = process_tts_synthesis(request, content, content_len);
                    if (WLSDK_RESULT_SUCCESS != resultcode) {
                        request_error_callback(request, resultcode);
                        wl_free(request);
                        wl_list_iterator_remove(&iterator);
                        break;
                    }
                }
                //下载完成
                else if (TTS_CLIENT_REQUEST_DOWNLOAD == request->state) {
                    if (wl_null != request->callback && wl_null != content) {
                        WLLOGD("tts_http_client_callback content len %d", content_len);
                        request->callback(
                            WLSDK_RESULT_SUCCESS, request->text, request->type, content, content_len, request->context);
                        /*wl_char* buffer = tts_read_file_data(content, &content_len);
                        if (buffer) {
                            request->callback(WLSDK_RESULT_SUCCESS,
                                              request->text,
                                              request->type,
                                              buffer,
                                              content_len,
                                              request->context);
                            wl_free(buffer);
                        } else {
                            request_error_callback(request, WLSDK_RESULT_LOCAL_FILE_ERROR);
                        }*/
                    }
                    wl_free(request);
                    wl_list_iterator_remove(&iterator);
                    break;
                }
            } else {
                request_error_callback(request, WLSDK_RESULT_LOCAL_REQUEST_TIMEOUT);
                wl_free(request);
                wl_list_iterator_remove(&iterator);
                break;
            }
        }
        request = (tts_client_request*)wl_list_get_next(&iterator);
    }
    execute_request();
}

static void request_error_callback(tts_client_request* request, wl_int32 result_code) {
    if (wl_null != request->callback) {
        request->callback(result_code, request->text, request->type, wl_null, 0, request->context);
    }
}

static void tts_client_timer_callback(WL_TIMER timer, void* context) {
    if (timer != s_tts_client_global.timer) {
        WLLOGW("timer %p is not equal local timer %p", timer, s_tts_client_global.timer);
        wl_timer_stop(timer);
        return;
    }
    {
        wl_list_iterator    iterator;
        tts_client_request* request =
            (tts_client_request*)wl_list_get_first(&s_tts_client_global.request_list, &iterator);
        while (!wl_list_iterator_is_end(&iterator)) {
            request->lifeleft -= TTS_CLIENT_TIMER_INTERVAL;
            //请求超时
            if (0 >= request->lifeleft) {
                WLLOGD("remove timeout request content %s", request->content);
                request_error_callback(request, WLSDK_RESULT_LOCAL_REQUEST_TIMEOUT);
                wl_free(request);
                request = (tts_client_request*)wl_list_iterator_remove(&iterator);
            } else {
                request = (tts_client_request*)wl_list_get_next(&iterator);
            }
        }
    }
    execute_request();
}

static wl_int32 execute_request(void) {
    wl_list_iterator    iterator;
    tts_client_request* request = (tts_client_request*)wl_list_get_first(&s_tts_client_global.request_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        //处理中
        if (TTS_CLIENT_REQUEST_INIT != request->state) {
            break;
        }
        request = (tts_client_request*)wl_list_get_next(&iterator);
    }
    //没有处理中tts,处理第一个
    if (wl_null == request) {
        request = (tts_client_request*)wl_list_get_first(&s_tts_client_global.request_list, &iterator);
        if (!wl_list_iterator_is_end(&iterator)) {
            request->state = TTS_CLIENT_REQUEST_SYNTHESIS;
            if (0
                != wl_http_client_post(request->url,
                                    request->timeout,
                                    request->content,
                                    request->content_len,
                                    HTTP_CLIENT_CONTENT_JSON,
                                    tts_http_client_callback,
                                    (void*)((int)request->seq))) {
                request_error_callback(request, WLSDK_RESULT_LOCAL_HTTP_ERROR);
                wl_free(request);
                wl_list_iterator_remove(&iterator);
                return -1;
            }
        }
    }

    //没有请求,停止定时器
    if (0 >= wl_list_size(&s_tts_client_global.request_list)) {
        if (WL_INVALID_TIMER != s_tts_client_global.timer) {
            wl_timer_stop(s_tts_client_global.timer);
            s_tts_client_global.timer = WL_INVALID_TIMER;
        }
    }

    return 0;
}

static wl_int32 add_request(wl_char*            url,
                            wl_char*            text,
                            wl_char*            content,
                            wl_uint32           content_len,
                            wl_uint32           timeout,
                            wl_int32            type,
                            tts_client_callback callback,
                            void*               context) {
    //请求超过限制
    if (TTS_CLIENT_MAX_REQUEST <= wl_list_size(&s_tts_client_global.request_list)) {
        WLLOGW("tts request too much");
        return -1;
    }
    if (strlen(text) >= MAX_TTS_TEXT_LEN) {
        WLLOGW("tts text too long");
        return -1;
    }
    //空队列
    if (0 >= wl_list_size(&s_tts_client_global.request_list)) {
        if (WL_INVALID_TIMER != s_tts_client_global.timer) {
            wl_timer_stop(s_tts_client_global.timer);
            s_tts_client_global.timer = WL_INVALID_TIMER;
        }
        //超时定时器
        s_tts_client_global.timer = wl_timer_start(TTS_CLIENT_TIMER_INTERVAL * 1000,
                                                   REPETITIVE,
                                                   tts_client_timer_callback,
                                                   wl_null,
                                                   WL_TASK_APP,
                                                   "tts_client");
        if (WL_INVALID_TIMER == s_tts_client_global.timer) {
            WLLOGW("wl_timer_start fail");
            return -1;
        }
    }
    //添加新请求
    {
        tts_client_request* client_request =
            (tts_client_request*)wl_malloc(sizeof(tts_client_request) + content_len + 1);
        if (wl_null == client_request) {
            return -1;
        }
        memset(client_request, 0, sizeof(tts_client_request));
        client_request->seq      = s_tts_client_global.request_seq++;
        client_request->state    = TTS_CLIENT_REQUEST_INIT;
        client_request->timeout  = timeout;
        client_request->lifeleft = timeout;
        client_request->type     = type;
        client_request->callback = callback;
        client_request->context  = context;
        strncpy(client_request->url, url, sizeof(client_request->url));
        client_request->url[sizeof(client_request->url) - 1] = '\0';
        strncpy(client_request->text, text, sizeof(client_request->text) - 1);
        client_request->text[sizeof(client_request->text) - 1] = '\0';
        memcpy(client_request->content, content, content_len);
        client_request->content[content_len] = '\0';
        client_request->content_len          = content_len;

        wl_list_add(&s_tts_client_global.request_list, client_request, -1);
    }
    execute_request();
    return 0;
}

wl_int32 tts_client_init(void) {
    WLLOGI("tts_client_init");
    if (!s_tts_client_global.initialized) {
        if (0 != wl_list_init(&s_tts_client_global.request_list)) {
            WLLOGW("wl_list_init fail");
            return -1;
        }
        s_tts_client_global.request_seq = 1;
        s_tts_client_global.initialized = wl_true;
    }
    return 0;
}

wl_int32 tts_client_end(void) {
    if (s_tts_client_global.initialized) {
        wl_list_iterator    iterator;
        tts_client_request* request =
            (tts_client_request*)wl_list_get_first(&s_tts_client_global.request_list, &iterator);
        while (!wl_list_iterator_is_end(&iterator)) {
            request_error_callback(request, WLSDK_RESULT_LOCAL_SYSTEM_RELEASE);
            request = (tts_client_request*)wl_list_get_next(&iterator);
        }
        wl_list_free(&s_tts_client_global.request_list);
        if (WL_INVALID_TIMER != s_tts_client_global.timer) {
            wl_timer_stop(s_tts_client_global.timer);
            s_tts_client_global.timer = WL_INVALID_TIMER;
        }
        s_tts_client_global.initialized = wl_false;
    }
    WLLOGI("tts_client_end");
    return 0;
}

wl_int32 tts_client_synthesis_ptt(wl_char*            text,
                                  wl_uint32           fromid,
                                  wl_uint64           sessionid,
                                  wl_uint32           sessiontype,
                                  wl_char*            people,
                                  wl_char*            place,
                                  wl_uint32           timeout,
                                  tts_client_callback callback,
                                  void*               context) {
    wl_char     tts_server_url[MAX_URL_LEN];
    wl_char     token[MAX_TOKEN_LEN + 1];
    WLcJSON*    body_obj = wl_null;
    wl_char*    content  = wl_null;
    repo_server web_server;

    repo_get_web_server(&web_server);
    repo_get_token(token);
    sprintf(tts_server_url,
            "http://%s:%d/v1/speech/embedded-synthesis?access-token=%s",
            web_server.host,
            web_server.port,
            token);
    body_obj = WLcJSON_CreateObject();
    if (wl_null == body_obj) {
        WLLOGW("create obj fail");
        return -1;
    }
    repo_build_http_sign(body_obj);
    WLcJSON_AddNumberToObject(body_obj, "from_id", fromid);
    WLcJSON_AddNumberToObject(body_obj, "session_id", sessionid);
    WLcJSON_AddNumberToObject(body_obj, "session_type", sessiontype);
    WLcJSON_AddStringToObject(body_obj, "people", people);
    WLcJSON_AddStringToObject(body_obj, "place", place);
    WLcJSON_AddStringToObject(body_obj, "message", text);
    content = WLcJSON_PrintUnformatted(body_obj);
    WLcJSON_Delete(body_obj);
    if (content) {
        wl_int32 ret = add_request(tts_server_url, text, content, strlen(content), timeout, TTS_PTT, callback, context);
        WLcJSON_free(content);
        return ret;
    }
    WLLOGW("WLcJSON_PrintUnformatted fail");
    return -1;
}

wl_int32 tts_client_synthesis_tts(wl_char* text, wl_uint32 timeout, tts_client_callback callback, void* context) {
    wl_char     tts_server_url[MAX_URL_LEN];
    WLcJSON*    body_obj = wl_null;
    wl_char*    content  = wl_null;
    repo_server web_server;

    repo_get_web_server(&web_server);
    sprintf(tts_server_url, "http://%s:%d/v1/speech/synthesis/tts", web_server.host, web_server.port);
    body_obj = WLcJSON_CreateObject();
    if (wl_null == body_obj) {
        WLLOGW("create obj fail");
        return -1;
    }
    repo_build_http_sign(body_obj);
    WLcJSON_AddStringToObject(body_obj, "text", text);
    content = WLcJSON_PrintUnformatted(body_obj);
    WLcJSON_Delete(body_obj);
    if (content) {
        wl_int32 ret = add_request(tts_server_url, text, content, strlen(content), timeout, TTS_TTS, callback, context);
        WLcJSON_free(content);
        return ret;
    }
    WLLOGW("WLcJSON_PrintUnformatted fail");
    return -1;
}

wl_bool tts_client_is_synthesis_ptt(void) {
    wl_list_iterator    iterator;
    tts_client_request* request = (tts_client_request*)wl_list_get_first(&s_tts_client_global.request_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        if (TTS_PTT == request->type) {
            return wl_true;
        }
        request = (tts_client_request*)wl_list_get_next(&iterator);
    }
    return wl_false;
}

#ifdef __cplusplus
}
#endif
